/**
 * File:   assets_preload_view_model.c
 * Author: AWTK Develop Team
 * Brief:  assets_preload_view_model
 *
 * Copyright (c) 2023 - 2024  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:
 * ================================================================
 * 2024-3-08 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#include "tkc/object.h"
#include "base/enums.h"
#include "conf_io/conf_node.h"
#include "tkc/func_call_parser.h"

#include "mvvm/mvvm.h"
#include "hmi_model.h"
#include "assets_preload_view_model.h"

#define ASSETS_PRELOAD(obj) assets_preload_cast(obj)

static assets_preload_t* assets_preload_cast(tk_object_t* obj);

static ret_t assets_preload_set_prop(tk_object_t* obj, const char* name, const value_t* v) {
  return_value_if_fail(obj != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);

  return RET_NOT_FOUND;
}

static ret_t assets_preload_get_prop(tk_object_t* obj, const char* name, value_t* v) {
  assets_preload_t* preload = ASSETS_PRELOAD(obj);
  return_value_if_fail(obj != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);

  if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_PROP_TOTAL)) {
    value_set_uint32(v, preload->total);
    return RET_OK;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_PROP_CURRENT_INDEX)) {
    value_set_uint32(v, preload->index);
    return RET_OK;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_PROP_DONE)) {
    value_set_bool(v, preload->done);
    return RET_OK;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_PROP_PROGRESS)) {
    if (preload->total == 0) {
      value_set_uint32(v, 100);
    } else {
      uint32_t progress = (preload->index * 100) / preload->total;
      value_set_uint32(v, progress);
    }
    return RET_OK;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_PROP_CURRENT_NAME)) {
    if (preload->current != NULL) {
      const char* current_name = conf_node_get_child_value_str(preload->current, "name", NULL);
      value_set_str(v, current_name);
    } else {
      value_set_str(v, "");
    }
    return RET_OK;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_PROP_CURRENT_TYPE)) {
    if (preload->current != NULL) {
      const char* current_type = conf_node_get_child_value_str(preload->current, "type", NULL);
      value_set_str(v, current_type);
    } else {
      value_set_str(v, "");
    }
    return RET_OK;
  }

  return RET_NOT_FOUND;
}

static ret_t assets_preload_on_destroy(tk_object_t* obj) {
  assets_preload_t* preload = ASSETS_PRELOAD(obj);
  return_value_if_fail(preload != NULL, RET_BAD_PARAMS);

  if (preload->timer_id != TK_INVALID_ID) {
    timer_remove(preload->timer_id);
  }
  conf_doc_destroy(preload->doc);

  return RET_OK;
}

static bool_t assets_preload_can_exec(tk_object_t* obj, const char* name, const char* args) {
  assets_preload_t* preload = ASSETS_PRELOAD(obj);
  return_value_if_fail(obj != NULL && name != NULL, FALSE);

  if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_CMD_START)) {
    return preload->doc != NULL && preload->timer_id == TK_INVALID_ID &&
           preload->index < preload->total;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_CMD_ABORT)) {
    return preload->timer_id != TK_INVALID_ID;
  }

  return FALSE;
}

ret_t assets_preload_step(assets_preload_t* preload) {
  const char* name = NULL;
  const char* type = NULL;

  return_value_if_fail(preload != NULL, RET_REMOVE);
  return_value_if_fail(preload->current != NULL, RET_REMOVE);

  name = conf_node_get_child_value_str(preload->current, "name", NULL);
  type = conf_node_get_child_value_str(preload->current, "type", NULL);

  if (name != NULL) {
    if (tk_str_eq(type, "image")) {
      bitmap_t img;
      if (image_manager_get_bitmap(image_manager(), name, &img) == RET_OK) {
        log_debug("load image ok: %s\n", name);
      } else {
        log_debug("load image failed: %s\n", name);
      }
    } else {
      const key_type_value_t* ret = asset_type_find(type);
      if (ret != NULL) {
        if (assets_manager_ref(assets_manager(), ret->value, name) != NULL) {
          log_debug("load asset ok: %s %s\n", type, name);
        } else {
          log_debug("load asset failed: %s %s\n", type, name);
        }
      } else {
        log_debug("unknown asset type: %s\n", type);
      }
    }
  }

  preload->index++;
  preload->current = preload->current->next;
  preload->done = preload->index >= preload->total;

  emitter_dispatch_simple_event(EMITTER(preload), EVT_PROPS_CHANGED);
  return preload->done ? RET_REMOVE : RET_REPEAT;
}

static ret_t preload_in_timer(const timer_info_t* info) {
  assets_preload_t* preload = ASSETS_PRELOAD(info->ctx);

  return assets_preload_step(preload);
}

static ret_t assets_preload_exec(tk_object_t* obj, const char* name, const char* args) {
  assets_preload_t* preload = ASSETS_PRELOAD(obj);
  return_value_if_fail(obj != NULL && name != NULL, RET_BAD_PARAMS);

  if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_CMD_START)) {
    uint32_t interval = 100;
    conf_doc_t* doc = preload->doc;
    return_value_if_fail(preload->total > 0, RET_OBJECT_CHANGED);

    interval = 3000 / preload->total;
    interval = conf_doc_get_int(doc, "interval", interval);
    preload->timer_id = timer_add(preload_in_timer, preload, interval);

    return RET_OBJECT_CHANGED;
  } else if (tk_str_ieq(name, ASSETS_PRELOAD_VIEW_MODEL_CMD_ABORT)) {
    preload->done = TRUE;
    return RET_OBJECT_CHANGED;
  }

  return RET_NOT_FOUND;
}

static const object_vtable_t s_assets_preload_vtable = {
    .type = "assets_preload",
    .desc = "assets_preload",
    .size = sizeof(assets_preload_t),
    .is_collection = FALSE,
    .on_destroy = assets_preload_on_destroy,
    .get_prop = assets_preload_get_prop,
    .set_prop = assets_preload_set_prop,
    .can_exec = assets_preload_can_exec,
    .exec = assets_preload_exec,
};

static tk_object_t* assets_preload_create(conf_doc_t* doc) {
  tk_object_t* obj = NULL;
  conf_node_t* assets = NULL;
  assets_preload_t* preload = NULL;
  return_value_if_fail(doc != NULL, NULL);

  obj = object_create(&s_assets_preload_vtable);
  return_value_if_fail(obj != NULL, NULL);
  preload = (assets_preload_t*)obj;
  preload->doc = doc;

  assets = conf_node_find_child(doc->root, "assets");
  if (assets != NULL) {
    preload->current = conf_node_get_first_child(assets);
    preload->total = conf_node_count_children(assets);
  }

  return obj;
}

static assets_preload_t* assets_preload_cast(tk_object_t* obj) {
  return_value_if_fail(obj != NULL, NULL);
  return_value_if_fail(obj->vt == &s_assets_preload_vtable, NULL);

  return (assets_preload_t*)obj;
}

tk_object_t* assets_preload_view_model_create_with_name(const char* name) {
  conf_doc_t* doc = NULL;
  tk_object_t* obj = NULL;
  char config_name[MAX_PATH + 1] = {0};

  tk_snprintf(config_name, MAX_PATH, "%s.json", name);
  doc = hmi_model_load_conf_from_asset(config_name);
  if (doc != NULL) {
    obj = assets_preload_create(doc);
    return obj;
  }

  return NULL;
}

view_model_t* assets_preload_view_model_create(navigator_request_t* req) {
  view_model_t* vm = NULL;
  tk_object_t* obj = NULL;
  const char* config = "preload";

  obj = assets_preload_view_model_create_with_name(config);
  return_value_if_fail(obj != NULL, NULL);

  vm = view_model_object_wrapper_create(obj);

  return vm;
}

ret_t assets_preload_view_model_deinit(void) {
  return RET_OK;
}

ret_t assets_preload_view_model_init(void) {
  return view_model_factory_register("assets_preload", assets_preload_view_model_create);
}
